package com.wjf.birdfish.util;

import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.QName;
import org.dom4j.io.DocumentResult;


/**
 * dom4j操作辅助类
 * 
 * @author liuzy
 * 
 */
public class Dom4jHelper {
	private static final List<Node> emptyList = new EmptyList<Node>();
	private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
	
	private static Transformer createTransformer() {
		Transformer transformer = null;
		try {
			transformer = TRANSFORMER_FACTORY.newTransformer();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		}
		return transformer;
	}
	
	/**
	 * 获取Document中元素的值
	 * 
	 * @param dom4jData
	 *            Document对象
	 * @param name
	 *            xpath形式的元素名（不含命名空间），注意name为子元素路径 <code>
	 * <?xml version="1.0" encoding="UTF-8"?>
	 * <root>
	 * 		<user>
	 * 			<name>liuzy</name>
	 * 			<age>23</age>
	 * 		</user>
	 * </root>
	 * String name = Dom4jHelper.getNodeValue(root, "user/name");
	 * </code>
	 * @return 元素值
	 */
	public static String getNodeValue(Document dom4jData, String name) {
		Node result = getNode(dom4jData, name);
		if (result == null) {
			return null;
		}
		return result.getText();
	}
	
	public static String getNodeValue(Element elem, String name) {
		Node result = getNode(elem, name);
		if (result == null) {
			return null;
		}
		return result.getText();
	}
	
	/**
	 * 设置Document中元素值，如果元素不存在，Dom4jHelper工具会以递归方式将元素创建出来
	 * 
	 * @param dom4jData
	 *            被操作的dom4j对象
	 * @param name
	 *            xpath形式的元素名（不含命名空间）
	 * @param value
	 *            元素值
	 */
	public static void setNodeValue(Document dom4jData, String name,
			String value) {
		if (value == null) {
			return;
		}
		Node result = getNode(dom4jData, name);
		if (result != null) {
			result.setText(value);
			return;
		}

		if (dom4jData == null || name == null || "".equals(name)) {
			return;
		}
		if (name.startsWith("/")) {
			name = name.substring(1);
		}
		String[] splits = name.split("/");
		StringBuilder xpath = new StringBuilder();
		Element parent = dom4jData.getRootElement();
		for (String split : splits) {
			xpath.append("/").append(split);
			Element node = (Element) getNode(dom4jData, xpath.toString());
			if (node == null) {
				node = DocumentHelper.createElement(new QName(split, parent.getNamespace()));
				parent.add(node);
			}
			parent = node;
		}
		parent.setText(value);
	}
	
	public static void setNodeValue(Element elem, String name,
			String value) {
		if (value == null) {
			return;
		}
		Node result = getNode(elem, name);
		if (result != null) {
			result.setText(value);
			return;
		}
		
		if (elem == null || name == null || "".equals(name)) {
			return;
		}
		if (name.startsWith("/")) {
			name = name.substring(1);
		}
		String[] splits = name.split("/");
		StringBuilder xpath = new StringBuilder();
		Element parent = elem;
		for (String split : splits) {
			xpath.append("/").append(split);
			Element node = (Element) getNode(elem, xpath.toString());
			if (node == null) {
				node = DocumentHelper.createElement(new QName(split, parent.getNamespace()));
				parent.add(node);
			}
			parent = node;
		}
		parent.setText(value);
	}
	
	/**
	 * 获取Document中的元素
	 * 
	 * @param dom4jData
	 *            被操作的Document的对象
	 * @param name
	 *            xpath形式的元素名（不含命名空间），注意name为子元素路径 
	 * <code>
	 * <?xml version="1.0" encoding="UTF-8"?>
	 * <root>
	 * 		<user>
	 * 			<name>liuzy</name>
	 * 			<age>23</age>
	 * 		</user>
	 * </root>
	 * Node name = Dom4jHelper.getNode(root, "user/name");
	 * </code>
	 * @return 取得的元素
	 */
	public static Node getNode(Document dom4jData, String name) {
		if (dom4jData == null) {
			return null;
		}
		if (name == null || "".equals(name)) {
			return null;
		}
		return getNode(dom4jData.getRootElement(), name);
	}
	
	public static Node getNode(Element elem, String name) {
		if (elem == null) {
			return null;
		}
		if (name == null || "".equals(name)) {
			return null;
		}
		@SuppressWarnings("unchecked")
		List<Element> children = elem.elements();
		for (Element child : children) {
			Element result = getElement(child, name);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

	/**
	 * 获取Document中的元素
	 * 
	 * @param dom4jData
	 *            被操作的Document的对象
	 * @param name
	 *            xpath形式的元素名（不含命名空间）
	 * @return 取得的元素
	 */
	public static Element getElement(Document dom4jData, String name) {
		if (dom4jData == null || name == null || "".equals(name)) {
			return null;
		}
		return getElement(dom4jData.getRootElement(), name);
	}

	/**
	 * 获取Element中的元素
	 * 
	 * @param elem
	 *            被操作的Element的对象
	 * @param name
	 *            xpath形式的元素名（不含命名空间）
	 * @return 取得的元素
	 */
	public static Element getElement(Element elem, String name) {
		if (elem == null || name == null || "".equals(name)) {
			return null;
		}
		if (name.startsWith("/")) {
			name = name.substring(1);
		}
		return getElement(elem, 0, name.split("/"));
	}

	private static Element getElement(Element elem, int index, String[] names) {
		if (elem == null || names == null || names.length == 0 || index < 0
				|| index >= names.length) {
			return null;
		}
		if (names[index] == null) {
			return null;
		}
		boolean match = names[index].equals(elem.getName());
		if (!match) {
			return null;
		}
		if ((++index) == names.length) {
			return elem;
		}
		if (index == names.length) {
			return null;
		}
		@SuppressWarnings("unchecked")
		Iterator<Node> nodes = elem.nodeIterator();
		while (nodes.hasNext()) {
			Object node = nodes.next();
			if (!Element.class.isInstance(node)) {
				continue;
			}
			Element nextElement = getElement((Element) node, index, names);
			if (nextElement != null) {
				return nextElement;
			}
		}
		return null;
	}

	public static Document transformToDocument(String text) {
		if (text == null) {
			return null;
		}
		return transformToDocument(text, sun.io.Converters.getDefaultEncodingName());
	}
	
	public static Document transformToDocument(InputStream in) {
		if (in == null) {
			return null;
		}
		return transformToDocument(in, sun.io.Converters.getDefaultEncodingName());
	}
	
	public static Document transformToDocument(InputStream in, String encoding) {
		Document result = null;
		Transformer transformer = createTransformer();
		if (encoding != null) {
			transformer.setParameter(OutputKeys.ENCODING, encoding);
		}
		DocumentResult resultProxy = new DocumentResult();
		try {
			transformer.transform(new StreamSource(in), resultProxy);
			result = resultProxy.getDocument();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public static Document transformToDocument(String text, String encoding) {
		Document result = null;
		Transformer transformer = createTransformer();
		if (encoding != null) {
			transformer.setParameter(OutputKeys.ENCODING, encoding);
		}
		DocumentResult resultProxy = new DocumentResult();
		try {
			transformer.transform(new StreamSource(new StringReader(text)), resultProxy);
			result = resultProxy.getDocument();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 获取Document中的元素
	 * 
	 * @param dom4jData
	 *            被操作的Document的对象
	 * @param name
	 *            xpath形式的元素名（不含命名空间）
	 * @return 以List形式返回所有匹配的元素
	 */
	@SuppressWarnings("unchecked")
	public static List<Node> getNodeForList(Document dom4jData, String name) {
		if (dom4jData == null) {
			return emptyList;
		}
		if (name == null || "".equals(name)) {
			return emptyList;
		}
		return dom4jData.selectNodes(createQueryKey(dom4jData, name));
	}

	private static String createQueryKey(Document dom4jData, String name) {
		String ns = getNS(dom4jData);
		String root = dom4jData.getRootElement().getName();
		return "/" + getPrefix(ns) + root + "/" + getPrefix(ns)
				+ name.replace("/", "/" + getPrefix(ns));
	}

	private static String getPrefix(String ns) {
		return ("".equals(ns) ? "" : ns + ":");
	}

	private static String getNS(Document doc) {
		return doc.getRootElement().getNamespace().getPrefix();
	}

	private static class EmptyList<T> implements List<T> {
		private List<T> local = new ArrayList<T>();

		public int size() {
			return local.size();
		}

		public boolean isEmpty() {
			return true;
		}

		public boolean contains(Object o) {
			return false;
		}

		public Iterator<T> iterator() {
			return local.iterator();
		}

		public Object[] toArray() {
			return local.toArray();
		}

		@SuppressWarnings("unchecked")
		public Object[] toArray(Object[] a) {
			return local.toArray(a);
		}

		public boolean add(Object e) {
			return false;
		}

		public boolean remove(Object o) {
			return false;
		}

		@SuppressWarnings("rawtypes")
		public boolean containsAll(Collection c) {
			return false;
		}

		@SuppressWarnings("rawtypes")
		public boolean addAll(Collection c) {
			return false;
		}

		@SuppressWarnings("rawtypes")
		public boolean addAll(int index, Collection c) {
			return false;
		}

		@SuppressWarnings("rawtypes")
		public boolean removeAll(Collection c) {
			return false;
		}

		@SuppressWarnings("rawtypes")
		public boolean retainAll(Collection c) {
			return false;
		}

		public void clear() {
		}

		public T get(int index) {
			return local.get(index);
		}

		public T set(int index, T element) {
			return local.set(index, element);
		}

		public void add(int index, T element) {
			// do nothing
		}

		public T remove(int index) {
			return local.remove(index);
		}

		public int indexOf(Object o) {
			return local.indexOf(o);
		}

		public int lastIndexOf(Object o) {
			return local.lastIndexOf(o);
		}

		public ListIterator<T> listIterator() {
			return local.listIterator();
		}

		public ListIterator<T> listIterator(int index) {
			return local.listIterator(index);
		}

		public List<T> subList(int fromIndex, int toIndex) {
			return local.subList(fromIndex, toIndex);
		}

	}
}
